<script>
// eslint-disable-next-line no-restricted-imports
import { mapState } from 'vuex';
import {
  GlTooltipDirective,
  GlBadge,
  GlTab,
  GlTabs,
  GlButton,
  GlModalDirective,
  GlFilteredSearchToken,
  GlDisclosureDropdown,
  GlDisclosureDropdownItem,
  GlAlert,
  GlLink,
  GlSprintf,
} from '@gitlab/ui';
import { s__, __, n__, sprintf } from '~/locale';
import { queryToObject, setUrlParams, updateHistory } from '~/lib/utils/url_utility';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import {
  ACTIVE_SUBTAB_QUERY_PARAM,
  ACTIVE_TAB_QUERY_PARAM_NAME,
  TAB_QUERY_PARAM_VALUES,
} from '~/members/constants';

import {
  PLACEHOLDER_USER_STATUS,
  PLACEHOLDER_USER_UNASSIGNED_STATUS_OPTIONS,
  PLACEHOLDER_USER_REASSIGNED_STATUS_OPTIONS,
  PLACEHOLDER_SORT_ID_ASC,
  PLACEHOLDER_SORT_ID_DESC,
  PLACEHOLDER_SORT_CREATED_AT_ASC,
  PLACEHOLDER_SORT_CREATED_AT_DESC,
  PLACEHOLDER_SORT_STATUS_DESC,
  PLACEHOLDER_SORT_STATUS_ASC,
  PLACEHOLDER_SORT_SOURCE_NAME_ASC,
  PLACEHOLDER_SORT_SOURCE_NAME_DESC,
  PLACEHOLDER_SORT_VALUES,
  PLACEHOLDER_TAB_AWAITING,
  PLACEHOLDER_TAB_REASSIGNED,
} from '~/import_entities/import_groups/constants';
import { helpPagePath } from '~/helpers/help_page_helper';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
import {
  FILTERED_SEARCH_TERM,
  OPERATORS_IS,
  TOKEN_TITLE_STATUS,
  TOKEN_TYPE_STATUS,
} from '~/vue_shared/components/filtered_search_bar/constants';
import BypassConfirmationMessage from './bypass_confirmation_message.vue';
import PlaceholdersTable from './placeholders_table.vue';
import CsvUploadModal from './csv_upload_modal.vue';
import KeepAllAsPlaceholderModal from './keep_all_as_placeholder_modal.vue';

const UPLOAD_CSV_PLACEHOLDERS_MODAL_ID = 'upload-placeholders-csv-modal';
const KEEP_ALL_AS_PLACEHOLDER_MODAL_ID = 'keep-all-as-placeholder-modal';
const mapCreatedAtToID = {
  [PLACEHOLDER_SORT_CREATED_AT_ASC]: PLACEHOLDER_SORT_ID_ASC,
  [PLACEHOLDER_SORT_CREATED_AT_DESC]: PLACEHOLDER_SORT_ID_DESC,
};

export default {
  name: 'PlaceholdersTabApp',
  components: {
    GlBadge,
    GlTab,
    GlTabs,
    GlButton,
    GlDisclosureDropdown,
    GlDisclosureDropdownItem,
    GlAlert,
    GlLink,
    GlSprintf,
    BypassConfirmationMessage,
    FilteredSearchBar,
    PlaceholdersTable,
    CsvUploadModal,
    KeepAllAsPlaceholderModal,
  },
  directives: {
    GlTooltip: GlTooltipDirective,
    GlModal: GlModalDirective,
  },
  inject: {
    group: {
      default: {},
    },
    allowBypassPlaceholderConfirmation: {
      default: null,
    },
  },
  data() {
    return {
      selectedTabIndex: 0,
      unassignedCount: null,
      reassignedCount: null,
      filterParams: {},
      sort: null,
      skipResettingFilterParams: false,
    };
  },
  computed: {
    ...mapState('placeholder', ['pagination']),
    initialFilterValue() {
      const { status, search } = this.filterParams || {};
      const filteredSearchValue = [];

      if (status) {
        filteredSearchValue.push({
          type: TOKEN_TYPE_STATUS,
          value: {
            data: status,
          },
        });
      }

      if (search) {
        filteredSearchValue.push({
          type: FILTERED_SEARCH_TERM,
          value: {
            data: search,
          },
        });
      }

      return filteredSearchValue;
    },
    urlParams() {
      return {
        [ACTIVE_TAB_QUERY_PARAM_NAME]: TAB_QUERY_PARAM_VALUES.placeholder,
        [ACTIVE_SUBTAB_QUERY_PARAM]:
          this.selectedTabIndex === 0 ? PLACEHOLDER_TAB_AWAITING : PLACEHOLDER_TAB_REASSIGNED,
        status: this.filterParams.status,
        search: this.filterParams.search,
        sort: this.sort,
      };
    },
    unassignedUserStatuses() {
      if (this.filterParams.status) {
        return [this.filterParams.status.toUpperCase()];
      }

      return PLACEHOLDER_USER_STATUS.UNASSIGNED;
    },
    reassignedUserStatuses() {
      if (this.filterParams.status) {
        return [this.filterParams.status.toUpperCase()];
      }

      return PLACEHOLDER_USER_STATUS.REASSIGNED;
    },
    sortOptions() {
      return [
        {
          id: 1,
          title: __('Status'),
          sortDirection: {
            descending: PLACEHOLDER_SORT_STATUS_DESC,
            ascending: PLACEHOLDER_SORT_STATUS_ASC,
          },
        },
        {
          id: 2,
          title: s__('UserMapping|Source name'),
          sortDirection: {
            descending: PLACEHOLDER_SORT_SOURCE_NAME_DESC,
            ascending: PLACEHOLDER_SORT_SOURCE_NAME_ASC,
          },
        },
        {
          id: 3,
          title: __('Created at'),
          sortDirection: {
            descending: PLACEHOLDER_SORT_CREATED_AT_DESC,
            ascending: PLACEHOLDER_SORT_CREATED_AT_ASC,
          },
        },
      ];
    },
    initialSortBy() {
      return this.sort || PLACEHOLDER_SORT_SOURCE_NAME_ASC;
    },
  },
  watch: {
    selectedTabIndex() {
      if (this.skipResettingFilterParams) {
        this.skipResettingFilterParams = false;
        return;
      }
      this.filterParams = {};
    },
    urlParams: {
      deep: true,
      handler(params) {
        if (Object.keys(params).length) {
          updateHistory({
            url: setUrlParams(params, {
              url: window.location.href,
              clearParams: true,
              decodeParams: true,
            }),
            title: document.title,
            replace: true,
          });
        }
      },
    },
  },
  created() {
    this.setInitialFilterAndSort();
  },
  mounted() {
    this.unassignedCount = this.pagination.awaitingReassignmentItems;
    this.reassignedCount = this.pagination.reassignedItems;
  },
  methods: {
    setInitialFilterAndSort() {
      const { sort, subtab, ...queryParams } = convertObjectPropsToCamelCase(
        queryToObject(window.location.search.substring(1), { gatherArrays: true }),
        {
          dropKeys: ['scope', 'utf8', 'tab'], // These keys are unsupported/unnecessary
        },
      );

      this.filterParams = { ...queryParams };

      if (sort && PLACEHOLDER_SORT_VALUES.includes(sort)) {
        this.sort = sort || PLACEHOLDER_SORT_SOURCE_NAME_ASC;
      }

      const reassignedStatuses = PLACEHOLDER_USER_REASSIGNED_STATUS_OPTIONS.map(
        (status) => status.value,
      );

      if (
        (queryParams.status && reassignedStatuses.includes(queryParams.status)) ||
        subtab === PLACEHOLDER_TAB_REASSIGNED
      ) {
        // When status param is one of the reassigned statuses, or subtab param is 'reassigned', open the reassigned tab
        this.skipResettingFilterParams = true;
        this.selectedTabIndex = 1;
      }
    },
    filteredSearchTokens(options = PLACEHOLDER_USER_UNASSIGNED_STATUS_OPTIONS) {
      return [
        {
          type: TOKEN_TYPE_STATUS,
          icon: 'status',
          title: TOKEN_TITLE_STATUS,
          unique: true,
          token: GlFilteredSearchToken,
          operators: OPERATORS_IS,
          options,
        },
      ];
    },
    onFilter(filters = []) {
      const filterParams = {};
      const plainText = [];

      filters.forEach((filter) => {
        if (!filter.value.data) return;

        switch (filter.type) {
          case TOKEN_TYPE_STATUS:
            filterParams.status = filter.value.data;
            break;
          case FILTERED_SEARCH_TERM:
            plainText.push(filter.value.data);
            break;
          default:
            break;
        }
      });

      if (plainText.length) {
        filterParams.search = plainText.join(' ');
      }

      this.filterParams = { ...filterParams };
    },
    onConfirm(item) {
      this.updateTabCount({ item, placeholderCount: 1 });
    },
    onConfirmKeepAllAsPlaceholders(placeholderCount) {
      this.updateTabCount({ placeholderCount });
    },
    updateTabCount({ item, placeholderCount }) {
      const message = item
        ? sprintf(
            s__('UserMapping|Placeholder %{name} (@%{username}) was kept as a placeholder.'),
            {
              name: item.placeholderUser.name,
              username: item.placeholderUser.username,
            },
          )
        : sprintf(
            n__(
              'UserMapping|%{count} placeholder user was kept as a placeholder.',
              'UserMapping|%{count} placeholder users were kept as placeholders.',
              placeholderCount,
            ),
            {
              count: placeholderCount,
            },
          );

      this.$toast.show(message);
      this.reassignedCount += placeholderCount;
      this.unassignedCount -= placeholderCount;
    },
    onSort(sort) {
      this.sort = sort;
    },
    /**
     * Maps created_at sort values to id sort values for backend queries.
     * This preserves user-friendly created_at url params
     * while using more efficient id-based sorting on the backend.
     */
    mapSortValue(sort) {
      return mapCreatedAtToID[sort] || sort;
    },
  },
  helpUrl: helpPagePath('user/import/mapping', {
    anchor: 'security-considerations',
  }),
  uploadCsvModalId: UPLOAD_CSV_PLACEHOLDERS_MODAL_ID,
  keepAllAsPlaceholderModalId: KEEP_ALL_AS_PLACEHOLDER_MODAL_ID,
  PLACEHOLDER_USER_REASSIGNED_STATUS_OPTIONS,
};
</script>

<template>
  <div>
    <gl-alert variant="warning" :dismissible="false" class="gl-mt-3">
      <gl-sprintf
        :message="
          s__(
            'UserMapping|Contribution and membership reassignment cannot be undone. Incorrect reassignment %{linkStart}poses a security risk%{linkEnd}, so check carefully before you reassign.',
          )
        "
      >
        <template #link="{ content }">
          <gl-link :href="$options.helpUrl" target="_blank">{{ content }}</gl-link>
        </template>
      </gl-sprintf>

      <p v-if="allowBypassPlaceholderConfirmation" class="gl-mb-0 gl-mt-3">
        <bypass-confirmation-message />
      </p>
    </gl-alert>
    <gl-tabs
      v-model="selectedTabIndex"
      nav-class="gl-grow gl-items-center gl-mt-3"
      content-class="gl-pt-0"
    >
      <gl-tab>
        <template #title>
          <span>{{ s__('UserMapping|Awaiting reassignment') }}</span>
          <gl-badge class="gl-tab-counter-badge">{{ unassignedCount || 0 }}</gl-badge>
        </template>

        <filtered-search-bar
          key="filter-unassigned"
          :namespace="group.path"
          :initial-filter-value="initialFilterValue"
          :initial-sort-by="initialSortBy"
          :tokens="filteredSearchTokens()"
          :sort-options="sortOptions"
          :search-input-placeholder="s__('UserMapping|Search placeholder users')"
          terms-as-tokens
          sync-filter-and-sort
          class="row-content-block gl-grow gl-border-t-0 @sm/panel:gl-flex"
          @onFilter="onFilter"
          @onSort="onSort"
        />
        <placeholders-table
          key="unassigned"
          data-testid="placeholders-table-unassigned"
          :query-statuses="unassignedUserStatuses"
          :query-search="filterParams.search"
          :query-sort="mapSortValue(sort)"
          @confirm="onConfirm"
        />
      </gl-tab>

      <gl-tab>
        <template #title>
          <span>{{ s__('UserMapping|Reassigned') }}</span>
          <gl-badge class="gl-tab-counter-badge">{{ reassignedCount || 0 }}</gl-badge>
        </template>

        <filtered-search-bar
          key="filter-reassigned"
          :namespace="group.path"
          :initial-filter-value="initialFilterValue"
          :initial-sort-by="initialSortBy"
          :tokens="filteredSearchTokens($options.PLACEHOLDER_USER_REASSIGNED_STATUS_OPTIONS)"
          :sort-options="sortOptions"
          :search-input-placeholder="s__('UserMapping|Search placeholder users')"
          terms-as-tokens
          sync-filter-and-sort
          class="row-content-block gl-grow gl-border-t-0 @sm/panel:gl-flex"
          @onFilter="onFilter"
          @onSort="onSort"
        />
        <placeholders-table
          key="reassigned"
          data-testid="placeholders-table-reassigned"
          :query-statuses="reassignedUserStatuses"
          :query-search="filterParams.search"
          :query-sort="mapSortValue(sort)"
          reassigned
        />
      </gl-tab>

      <template #toolbar-end>
        <div class="gl-ml-auto gl-flex gl-gap-2">
          <gl-button
            v-gl-modal="$options.uploadCsvModalId"
            icon="media"
            data-testid="reassign-csv-button"
          >
            {{ s__('UserMapping|Reassign with CSV file') }}
          </gl-button>
          <csv-upload-modal :modal-id="$options.uploadCsvModalId" />
          <gl-disclosure-dropdown
            v-gl-tooltip.hover.focus="__('More actions')"
            icon="ellipsis_v"
            category="tertiary"
            no-caret
            text-sr-only
            :toggle-text="__('More actions')"
            :auto-close="false"
          >
            <gl-disclosure-dropdown-item
              v-gl-modal="$options.keepAllAsPlaceholderModalId"
              data-testid="keep-all-as-placeholder-button"
            >
              <template #list-item>
                {{ s__('UserMapping|Keep all as placeholders') }}
              </template>
            </gl-disclosure-dropdown-item>
          </gl-disclosure-dropdown>
          <keep-all-as-placeholder-modal
            :modal-id="$options.keepAllAsPlaceholderModalId"
            :group-id="group.id"
            @confirm="onConfirmKeepAllAsPlaceholders"
          />
        </div>
      </template>
    </gl-tabs>
  </div>
</template>
